package com.blacktry.GATest;


import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

public class GeneticAlgorithm {
    private int populationSize;
    private double mutationRate;
    private double crossoverRate;
    private int elitismCount;
    // 退火算法
    private double coolingRate;
    private double temperature = 1.0;

    protected int tournamentSize;

    public double getTempature(){
        return this.temperature;
    }

    public void coolTemperature(){
        this.temperature *= (1-this.coolingRate);
    }

    // 已验
    public GeneticAlgorithm
            (int populationSize, double mutationRate, double crossoverRate, int elitismCount, int tournamentSize) {
        this.populationSize = populationSize;
        this.mutationRate = mutationRate;
        this.crossoverRate = crossoverRate;
        this.elitismCount = elitismCount;
        this.tournamentSize = tournamentSize;
    }

    // 已验
    public Population initPopulation(Timetable timetable) {
        Population population = new Population(this.populationSize, timetable);
        return population;
    }

    // 均匀交叉  已验
//    public Population crossoverPopulation(Population population) {
//        Population newPopulation = new Population(population.size());
//        for (int populationIndex = 0; populationIndex < population.size();
//             populationIndex++) {
//            Individual parent1 = population.getFittest(populationIndex);
//            if (this.crossoverRate > Math.random() && populationIndex >
//                    this.elitismCount) {
//                Individual offspring = new Individual(parent1.getChromosome());
//                Individual parent2 = selectParent(population);
//                for (int geneIndex = 0; geneIndex < parent1.
//                        getChromosomeLength(); geneIndex++) {
//                    if (0.5 > Math.random()) {
//                        offspring.setGene(geneIndex,
//                                parent1.getGene(geneIndex));
//                    } else {
//                        offspring.setGene(geneIndex,
//                                parent2.getGene(geneIndex));
//
//                    }
//                }
//                newPopulation.setIndividual(populationIndex,
//                        offspring);
//
//            } else {
//                newPopulation.setIndividual
//                        (populationIndex, parent1);
//            }
//        }
//        return newPopulation;
//    }
    public Population crossoverPopulation(Population population) {
        // 单点交叉
                Population newPopulation = new Population(population.size());
        for (int populationIndex = 0; populationIndex < population.size();
             populationIndex++) {
            Individual parent1 = population.getFittest(populationIndex);
            if (this.crossoverRate > Math.random() && populationIndex >=
                    this.elitismCount) {
                Individual offspring = new Individual(parent1.
                        getChromosomeLength());
                Individual parent2 = this.selectParent(population);

                int swapPoint = (int)(Math.random()*(parent1.getChromosomeLength()+1));

                for (int geneIndex = 0; geneIndex < parent1.
                        getChromosomeLength(); geneIndex++) {
                    if (geneIndex < swapPoint) {
                        offspring.setGene(geneIndex,
                                parent1.getGene(geneIndex));
                    } else {
                        offspring.setGene(geneIndex,
                                parent2.getGene(geneIndex));
                    }
                }
                newPopulation.setIndividual(populationIndex, offspring);
            } else {
                newPopulation.setIndividual
                        (populationIndex, parent1);
            }
        }
        return newPopulation;
    }

    // 竞标赛选择  已验
    public Individual selectParent(Population population) {
        Population tournament = new Population(this.tournamentSize);
        population.shuffle();
        for (int i = 0; i < this.tournamentSize; i++) {
            Individual tournamentIndividual = population.getIndividual(i);
            tournament.setIndividual(i,tournamentIndividual);
        }
        return tournament.getFittest(0);

//        Individual individuals[] = population.getIndividuals();
//        double populationFitness = population.getPopulationFitness();
//        double rouletteWheelPosition = Math.random() * populationFitness;
//        double spinWheel = 0;
//        for (Individual individual : individuals) {
//            spinWheel += individual.getFitness();
//            if (spinWheel >= rouletteWheelPosition) {
//                return individual;
//            }
//        }
//        return individuals[population.size() - 1];
    }

    // 已验
    public double calcFitness(Individual individual, Timetable timetable) {

        Double storedFitness = this.fitnessHash.get(individual);
        if(storedFitness != null){
            return storedFitness;
        }
        Timetable threadTimetable = new Timetable(timetable);
        threadTimetable.createClasses(individual);
        // 适应度和冲突起反比
        int clashes = threadTimetable.calcClashes();
        double fitness = 1 / (double) (clashes + 1);
        individual.setFitness(fitness);
        this.fitnessHash.put(individual,fitness);
        return fitness;
    }

    // 已验
    public void evalPopulation(Population population, Timetable timetable) {
//        IntStream.range(0, population.size()).parallel().forEach(i -> this.calcFitness(population.getIndividual(i), timetable));

        double populationFitness = 0;
        for (Individual individual : population.getIndividuals()) {
            populationFitness += this.calcFitness(individual,timetable);
//            populationFitness += individual.getFitness();
        }
        population.setPopulationFitness(populationFitness);
    }

    // 已验
    public boolean isTerminationConditionMet(Population population) {
        return population.getFittest(0).getFitness() == 1.0;
    }

    // 已验
    public boolean isTerminationConditionMet(int generationsCount, int maxGenerations) {
        return (generationsCount > maxGenerations);
    }

    // 已验
    public Population mutatePopulation(Population population, Timetable timetable) {
        Population newPopulation = new Population(this.populationSize);

        double bestFitness = population.getFittest(0).getFitness();

        for (int populationIndex = 0; populationIndex < population.size(); populationIndex++) {
            Individual individual = population.getFittest(populationIndex);
            Individual randomIndividual = new Individual(timetable);

            double adaptiveMutationRate = this.mutationRate;
            if (individual.getFitness() > population.getAvgFitness()) {
                double fitnessDelta1 = bestFitness - individual.getFitness();
                double fitnessDelta2 = bestFitness - population.getAvgFitness();
                adaptiveMutationRate = (fitnessDelta1 / fitnessDelta2) * this.mutationRate;
            }

            for (int geneIndex = 0; geneIndex < individual.getChromosomeLength(); geneIndex++) {
                // 跳过精英个体
                if (populationIndex > this.elitismCount) {
                    // 引入退火
                    if (adaptiveMutationRate * this.getTempature() > Math.random()) {
//                    if (adaptiveMutationRate > Math.random()) {
                        individual.setGene(geneIndex, randomIndividual.getGene(geneIndex));
                    }

//                    if(this.mutationRate>Math.random()){
//                        individual.setGene(geneIndex, randomIndividual.getGene(geneIndex));
//                    }
                }
            }
            newPopulation.setIndividual(populationIndex, individual);
        }
        return newPopulation;
    }

    // 已验
    private Map<Individual, Double> fitnessHash = Collections.
            synchronizedMap(
            new LinkedHashMap<Individual, Double>() {
                @Override
                protected boolean removeEldestEntry(Map.Entry<Individual, Double> eldest) {

                    return this.size() > 1000;
                }
            });


}
